<?php

namespace service\proxy;

use service\proxy\curl as http;
use service\proxy\redis as Redis;
/**
 * 代理列表维护类,基于redis
 */
class proxylist
{
    /**
     * redis instance
     *
     * @var null
     */
    // private static $redis = null;

    /**
     * api_uri
     *
     * @var string
     */
    private static $api_uri = 'http://ent.kuaidaili.com/api/getproxy';

    /**
     * api_common_params
     *
     * @var array
     */
    private static $api_common_params = [
        'orderid' => '',
        'num' => '',
    ];

    private static $api_params = [
        'quality' => 2, //代理ip的稳定性 0: 不筛选(默认) 1: VIP稳定  2: SVIP企业版非常稳定
        // 'area' => '', //ip所在地区，支持按 国家/省/市 筛选  多个地区用英文逗号分隔，如 北京,上海
        'protocol' => '2', // 按代理协议筛选 1: HTTP, 2: HTTPS(同时也支持HTTP)
        //'method' => '1',  //按支持 GET/POST 筛选 1: 支持HTTP GET, 2: 支持 HTTP POST(同时也支持GET)
        'dedup' => 1, //  过滤今天提取过的IP  取值固定为1
        'an_ha' => 1, //返回的代理中只包含高匿名代理  取值固定为1
        'an_an' => 1, //返回的代理中只包含匿名代理   取值固定为1
        //'an_tr' => 1, //返回的代理中只包含透明代理   取值固定为1
        'sp1' => 1, //返回的代理中只包含极速代理（响应速度<1秒）  取值固定为1
        'sp2' => 1, //返回的代理中只包含快速代理（响应速度1~3秒） 取值固定为1
        //'sp3' => 1, //返回的代理中只包含慢速代理（响应速度>3秒）  取值固定为1
        'sort' => 1, // 返回的代理列表的排序方式    0: 默认排序  1: VIPSVIP企业版按响应速度(从快到慢)
        'format' => 'json', // 接口返回内容的格式   text: 文本格式(默认), json: VIPSVIP企业版json格式 xml : VIPSVIP企业版xml格式
        // 'sep' => '', // 提取结果列表中每个结果的分隔符 1: \r\n分隔(默认) 2: \n分隔  3: 空格分隔  4: |分隔
    ];

    private $last_ip;
    /**
     * good ip store key
     * @var string
     */
    const GOOD_IP = 'good_ips';
    /**
     * bad ip store key
     * @var string
     */
    const BAD_IP = 'bad_ips';
    /**
     * good ip update time
     * @var string
     */
    const UPDATE_TIME = 'update_time';

    /**
     * minimal good ip number
     * @var integer
     */
    const MIN_GOOD_IP_NUM = 50;

    /**
     * ip expires time
     * @var integer
     */
    const EXPIRES_TIME = 3600;

    /**
     * init redis
     */
    // public function __construct()
    // {
    //     if (self::$redis === null) {
    //         //self::$redis = (new Redis())->getInstance();
    //     }
    // }

    /**
     * set api request params
     *
     * @param string|array $key
     * @param string $val
     * @return mixed
     */
    public static function setApiParams($key, $val = null)
    {
        if(is_array($key)){
            return self::$api_params = array_merge(self::$api_params, $key);
        }elseif(is_string($key) && is_scalar($val)){
            return self::$api_params[$key] = $val;
        }
        return false;
    }

    /**
     * getIpPoolLength
     *
     * @param string $type
     * @return integer
     */
    public static function getIpPoolLength($type = 'good')
    {
        return Redis::sCard(self::keyBuilder($type));
    }

    /**
     * get a ip
     *
     * @return mixed
     */
    public static function get()
    {
        if($this->getIpPoolLength() <= self::MIN_GOOD_IP_NUM){
            $this->update();
        }
        return $this->last_ip = Redis::sRandMember(self::keyBuilder(self::GOOD_IP))?:'alerm no ip found';
    }

    /**
     * getLastGetIp
     * @return string|null
     */
    public function getLastGetIp()
    {
        return $this->last_ip;
    }

    /**
     * get all ips
     *
     *  @return array|null
     */
    public static function getAll()
    {
        return Redis::sMembers(self::keyBuilder(self::GOOD_IP));
    }

    /**
     * mannul update ip list
     *
     * @param array $params
     * @return boolean
     */
    public static function renew(array $params = [])
    {
        return static::update(array_merge(self::$api_params, (array) $params), true);
    }

    /**
     * drop some ips from good ip list
     *
     * @param  string|array $ips
     * @return test
     */
    public static function drop($ips)
    {
        if (is_string($ips)) {
            $ips = [$ips];
        }
        if (is_array($ips)) {
            // $_bad_ip_key = self::keyBuilder(self::BAD_IP);
            // $_good_ip_key = self::keyBuilder(self::GOOD_IP);
            $_store_key = self::keyBuilder(self::GOOD_IP);
            foreach ($ips as $val) {
                //Redis::sMove($_good_ip_key, $_bad_ip_key, $val);
                Redis::sRem($_store_key, $val);
            }
        }
    }

    /**
     * update ip
     *
     * @param array $params
     * @param  boolean $force
     * @return boolean|int
     */
    private static function update(array $params = [], $force = false)
    {
        if ($force || (int) self::getUpdateTime() > time() + self::$update_interval) {
            if ($data = http::get(self::buildRequestUri(array_merge(self::$api_params, (array) $params)))) {
                if ($data = json_decode($data, true) and $data['data']['count'] > 0) {
                    self::setUpdateTime();
                    return Redis::sAdd(self::keyBuilder(self::GOOD_IP), $data['data']['proxy_list']);
                }else{
                    //@todo write log
                    //alerm
                }
            }else{
                //request failed
                //alerm
            }
        }
        return false;
    }

    /**
     * buildRequestUri
     *
     * @param array $params custom parameters for api call
     * @return string
     */
    private static function buildRequestUri(array $params = [])
    {
        return $api_uri . '?' . http_build_query(array_merge(self::$api_common_params, $params));
    }

    /**
     * getUpdateTime
     *
     * @return integer
     */
    private static function getUpdateTime()
    {
        return Redis::get(self::keyBuilder(self::UPDATE_TIME));
    }

    /**
     * setUpdateTime
     *
     * @return void
     */
    private static function setUpdateTime()
    {
        return Redis::set(self::keyBuilder(self::UPDATE_TIME), time(), 86400);
    }

    /**
     * redis store keyBuilder
     *
     * @param string $key
     * @return string
     */
    private static function keyBuilder($key = null)
    {
        if (!array_key_exists($key, self::$keys)) {
            raise(2, '参数配置错误');
        }
        return '__express_proxy_list__' . self::$keys[$key];
    }

}
